function Array2D(x,y)
{
	 this.items = new Array(x);
	 for(i=0;i<y;i++){this.items[i] = new Array(y);}
};

function dist(x1,y1,x2,y2)
{
	var x = x2 - x1;
	var y = y2 - y1;
	return Math.sqrt(x*x + y*y);
}

function angle(x1,y1,x2,y2)
{
	var top = x1*x2 + y1*y2;
	return top/(dist(0,0,x1,y1) * dist(0,0,x2,y2));
}

function Tile(x, y, h, parent){
	this.x = x;
	this.y = y;
	
	this.g = 0;
	this.h = h;
	
	this.parent = parent;
	
	this.getG = function(){
		if(this.parent != 0)
		{
			if(this.g != 0)
				return this.g;
			this.g = 10 + this.parent.getG();
				return 10 + this.parent.getG();
		}
		else
		{
			G = 0;
			return 0;
		}
	}	
}

function List(){
	this.items = new Array();
	this.length = 0;
	
	this.add = function(item){
		this.items[this.length] = item;
		this.length++;
	}
	
	this.removeAt = function(pos){
		for(var i = pos; i < this.length - 1; i++)
		{
			this.items[i] = this.items[i+1];
		}
		this.length--;
		if(this.length == 0)
			this.items = new Array();
		else
			this.items = this.items.slice(0, this.length);
	}
	this.remove = function(item){
		for(var i = 0; i < this.length; i++)
		{
			if(this.items[i] == item)
			{
				this.removeAt(i);
				return;
			}
		}
	}
}

var Game = {};

Game.fps = 60;
Game.isRunning = true;
Game.player = new Player(50,50, 20);
Game.field = new Array(100);
Game.playerWaypoints = 0;
Game.waypointIndex =1;
Game.counter = 0;
Game.mouseX = 0;
Game.mouseY = 0;
Game.regTimer = 60;
Game.fov = 0.4;
Game.maxRegTimer = 35;
Game.trapSound = new Audio("http://mazemorph.googlecode.com/svn/trunk/trap.ogg");
Game.gameOver = false;
Game.fadeToRedTimer = 0;
Game.won = false;
Game.fadeToGreenTimer = 0;
Game.endX = 0;
Game.endY = 0;
Game.endTimer = 600;

Game.setTile = function(x,y,tile){
	Game.field[x][y] = tile; 
};

Game.updateField = function(){

}

Game.findPath = function(sX,sY,eX,eY){
	if(Game.field[eX][eY] == 1)
		return;
	sX = Math.round(sX);
	sY = Math.round(sY);
	var openFields = new List();
	var closedFields = new List();
	
	var firstTile = new Tile(sX, sY, dist(sX,sY,eX,eY), 0);
	openFields.add(firstTile);
	
	var done = false;
	while(!done)
	{
		if(openFields.length == 0)
			return;
		var lowestF = openFields.items[0].getG() + openFields.items[0].h;
		var lowestIndex = 0;
		for(var i = 1 ; i < openFields.length; i++)
		{
			openFields.items[i].getG();
			if(openFields.items[i].getG() + openFields.items[i].h < lowestF)
			{
				lowestF = openFields.items[i].getG() + openFields.items[i].h;
				lowestIndex = i;
			}
		}
		
		closedFields.add(openFields.items[lowestIndex]);
		openFields.removeAt(lowestIndex);
		var lastTile = closedFields.items[closedFields.length - 1];
		var lowestTile = new Tile(lastTile.x, lastTile.y, lastTile.h, lastTile.parent);

		for(var x = lowestTile.x - 1; x <= lowestTile.x + 1; x++)
		{
			if(x < 0 || x >= 99)
				continue;

			for(var y = lowestTile.y - 1; y <= lowestTile.y + 1; y++)
			{
				if(y < 0 || y >= 99)
					continue;

				if(y == lowestTile.y && x == lowestTile.x)
					continue;
				if(y != lowestTile.y && x != lowestTile.x)
					continue;

				if(y == eY && x == eX)
				{
					var endTile = new Tile(x,y,0,lowestTile);
					openFields.add(endTile);
					done = true;
					break;
				}

				if(Game.field[x][y] != 1)
				{
					var inClosed = false;
					for(var i = 0 ; i < closedFields.length; i++)
					{
						if(closedFields.items[i].x == x && closedFields.items[i].y == y)
						{
							inClosed = true;
							break;
						}
					}
					if(inClosed)
						continue;
					
					var newTile = new Tile(x,y,dist(x,y,eX,eY),lowestTile);
					var inOpen = false;
					for(var i = 0 ; i < openFields.length; i++)
					{
						if(openFields.items[i].x == x && openFields.items[i].y == y)
						{
							inOpen = true;
							if(openFields.items[i].getG() > newTile.getG())
							{
								openFields.items[i].parent = newTile.parent;
							}
						}
					}
					
					if(!inOpen)
						openFields.add(newTile);
				}
			}
			if(done)
				break;
		}
	}
	
	Game.playerWaypoints = new List();
	
	var curr = openFields.items[openFields.length - 1];
	while(curr != 0)
	{
		Game.playerWaypoints.add(new Tile(curr.x, curr.y, 0,0));
		curr = curr.parent;
	}
	Game.playerWaypoints.items.reverse();
	return;
}

Game.movePlayerTo = function(x,y){
	if(!Game.gameOver && !Game.won)
	{
		var mapX = Math.floor(x /50) + (Math.round(Game.player.x) -8);
		var mapY = Math.floor(y /50) + (Math.round(Game.player.y) -8);
		Game.waypointIndex = 1;
		Game.playerWaypoints = 0;
		Game.findPath(Game.player.x,Game.player.y,mapX,mapY);
	}
}

Game.setMousePos = function(x,y){
	if(!Game.gameOver && !Game.won)
	{
		Game.mouseX = Math.floor(x /50) + (Math.round(Game.player.x) -8);
		Game.mouseY = Math.floor(y /50) + (Math.round(Game.player.y) -8);
	}
}

Game.initialize = function() {
	Game.fps = 60;
	Game.isRunning = true;
	Game.player = new Player(50,50, 20);
	Game.field = new Array(100);
	Game.playerWaypoints = 0;
	Game.waypointIndex =1;
	Game.counter = 0;
	Game.mouseX = 0;
	Game.mouseY = 0;
	Game.regTimer = 60;
	Game.fov = 0.4;
	Game.maxRegTimer = 35;
	Game.trapSound = new Audio("http://mazemorph.googlecode.com/svn/trunk/trap.ogg");
	Game.gameOver = false;
	Game.fadeToRedTimer = 0;
	Game.won = false;
	Game.fadeToGreenTimer = 0;
	Game.endX = 0;
	Game.endY = 0;
	Game.endTimer = 600;

	this.context = document.getElementById("view").getContext("2d");
	this.context.clearRect(0, 0, 800, 800);
	
	this.context.font = "bold 20px Arial";
	for (var x = 0; x < 100; x = x + 1)
	{
		Game.field[x] = new Array(100);
		for(var y = 0; y < 100; y = y + 1)
		{
			var ran = Math.random();
			if(ran > 0.4)
				Game.field[x][y] = 1;
			else if(ran > 0.37)
				Game.field[x][y] = 2;
			else
				Game.field[x][y] = 0;
		}	
	}
	Game.player.x = 50;
	Game.player.y = 50;
	
	var randX = Math.round(Math.random() * 40);
	var randY = Math.round(Math.random() * 40);
	var offsetX = randX - 20;
	var offsetY = randY - 20;
	var endX = offsetX + Game.player.x;
	var endY = offsetY + Game.player.y;
	Game.endX = endX;
	Game.endY = endY;
	Game.field[endX][endY] = 3;
	Game.field[Game.player.x][Game.player.y] = 0;
};

Game.draw = function() {
	if(!Game.isRunning)
		return;
		
	if(!Game.gameOver && !Game.won)
		Game.regTimer--;
	else
	{
		if(Game.gameOver)
			Game.fadeToRedTimer++;
		else
			Game.fadeToGreenTimer+=2;
	}
		
	this.context.clearRect(0, 0, 800, 800);
	this.context.fillStyle= "rgb(125,125,125)";
	this.context.fillRect(0, 0, 800, 800);
	
	for (var x = -1; x < 17; x = x + 1)
	{
		for(var y = -1; y < 17; y = y + 1)
		{
			var mapX = x+ Math.floor(Game.player.x) - 8;
			var mapY = y+ Math.floor(Game.player.y) - 8;
			var posAngle = angle(mapX-Math.round(Game.player.x),mapY-Math.round(Game.player.y),Game.mouseX-Math.round(Game.player.x),Game.mouseY-Math.round(Game.player.y));
			
			var inPath = false;
			if(Game.playerWaypoints != 0)
			{
				for(var i = 0; i < Game.playerWaypoints.length; i++)
				{
					if(mapX == Game.playerWaypoints.items[i].x && mapY == Game.playerWaypoints.items[i].y)
					{
						inPath = true;
						break;
					}
				}
			}
			
			if(posAngle < Game.fov || (Game.mouseX == Game.player.x && Game.mouseY == Game.player.y) )
			{
				if(Game.regTimer > 0 && Game.field[mapX][mapY] == 1)
					this.context.fillStyle= "rgb(20,20,20)";
				else
					this.context.fillStyle= "rgb(50,50,50)";
					
				if(Game.regTimer == 0 && Game.field[mapX][mapY] != 3 && !inPath && !(mapX == Math.round(Game.player.x) && mapY == Math.round(Game.player.y)))
				{
					var ran = Math.random();
					if(ran > 0.4)
						Game.field[mapX][mapY] = 1;
					else if(ran > 0.37)
						Game.field[mapX][mapY] = 2;
					else
						Game.field[mapX][mapY] = 0;
				}
			}
			else if(mapX < 0 || mapX > 99 || mapY < 0 || mapY > 99)
				this.context.fillStyle= "rgb(0,0,0)";
			else if(Game.field[mapX][mapY] == 1)
				this.context.fillStyle= "rgb(0,0,0)";
			else if(Game.field[mapX][mapY] == 3)
			{
				this.context.fillStyle= "rgb(0,0,255)";
				if(mapX == Math.round(Game.player.x) && mapY == Math.round(Game.player.y))
				{
					Game.won = true;
					var successSnd = new Audio("http://mazemorph.googlecode.com/svn/trunk/success.ogg")
					successSnd.play();
				}
			}
			else if(Game.field[mapX][mapY] == 2 && dist(Game.player.x, Game.player.y, mapX,mapY) <= 3)
			{
				this.context.fillStyle= "rgb(120,0,0)";
				if(mapX == Math.round(Game.player.x) && mapY == Math.round(Game.player.y))
				{
					Game.fov += 0.1;
					Game.maxRegTimer -= 5;
					Game.field[mapX][mapY] = 0;
					
					if(Game.fov > 0.8)
					{
						Game.gameOver = true;
						var deathSnd = new Audio("http://mazemorph.googlecode.com/svn/trunk/trap-deadly.ogg")
						deathSnd.play();
					}
					else
						Game.trapSound.play();
						
				}
			}
			else if(mapX == Game.mouseX && mapY == Game.mouseY)
				this.context.fillStyle= "rgb(200,200,200)";
			else if(inPath)
				this.context.fillStyle= "rgb(120,50,120)";
			else
				this.context.fillStyle= "rgb(125,125,125)";
				
			this.context.fillRect(Math.floor(x*50 -  (Game.player.x*50 - Math.floor(Game.player.x)*50)), Math.floor(y*50 -(Game.player.y*50 - Math.floor(Game.player.y)*50)), 50, 50);
		}	
	}
	if(Game.regTimer == 0)
		Game.regTimer = Game.maxRegTimer;
	
	this.context.save();
	var dirToTargetX = Game.endX - Game.player.x;
	var dirToTargetY = Game.endY - Game.player.y;
	
	dirToTargetX /= dist(0,0,dirToTargetX,dirToTargetY);
	dirToTargetY /= dist(0,0,dirToTargetX,dirToTargetY);
	
	var dot = angle(1,0,dirToTargetX,dirToTargetY);
	var rot = Math.acos(dot);
	if(dirToTargetY > 0)
		rot = 2*Math.pi - rot;
	
	this.context.translate(8*50 + 12,8*50 + 12);
	this.context.translate(dirToTargetX*100, dirToTargetY*100);
	this.context.rotate(rot);
	this.context.fillStyle= "rgb(0,255,0)";
	this.context.fillRect(-5,-5,10,10);
	this.context.restore();
	
	this.context.fillStyle= "rgb(0,0,255)";
	this.context.fillRect(8*50 + 12,8*50 + 12,25,25);
	
	this.context.fillStyle= "rgb(255,0,0)";
	this.context.globalAlpha = Game.fadeToRedTimer/255;
	this.context.fillRect(0,0,800,800);
	this.context.globalAlpha = 1;
	
	this.context.fillStyle= "rgb(0,255,0)";
	this.context.globalAlpha = Game.fadeToGreenTimer/255;
	this.context.fillRect(0,0,800,800);
	this.context.globalAlpha = 1;
	
	if(Game.fadeToRedTimer >= 125)
	{
		this.context.fillStyle= "rgb(0,0,0)";
		this.context.fillText("You stepped on a trap and died...", 280, 300);
		this.context.fillText("Click to play again!", 330, 400);
		Game.snd.pause();
		Game.isRunning = false;
	}
	
	if(Game.fadeToGreenTimer >= 125)
	{
		this.context.fillStyle= "rgb(0,0,0)";
		this.context.fillText("You found your way out of the labyrinth and are still alive!", 170, 300);
		this.context.fillText("Click to play again!", 330, 400);
		Game.snd.pause();
		Game.isRunning = false;
	}
	
};

Game.reset = function()
{
	Game.isRunning = true;
	Game.player = new Player(14,14, 20);
	Game.field = new Array(100);
	Game.snd.play();
	Game.initialize();
}

Game.update = function()
{
	if(!Game.isRunning)
		return;
	
	if(Game.playerWaypoints != 0)
	{
		var x = Game.playerWaypoints.items[Game.waypointIndex].x - Game.player.x;
		var y = Game.playerWaypoints.items[Game.waypointIndex].y - Game.player.y;
		
		var dirX = x/dist(0,0,x,y);
		var dirY = y/dist(0,0,x,y);
		
		Game.player.x += dirX/15.0;
		Game.player.y += dirY/15.0;
		
		if(dist(Game.player.x, Game.player.y, Game.playerWaypoints.items[Game.waypointIndex].x, Game.playerWaypoints.items[Game.waypointIndex].y) < 0.1)
		{
			Game.waypointIndex++;
			if(Game.waypointIndex == Game.playerWaypoints.length)
			{
				Game.player.x = Game.playerWaypoints.items[Game.waypointIndex-1].x;
				Game.player.y = Game.playerWaypoints.items[Game.waypointIndex-1].y;
				Game.waypointIndex = 1;
				Game.playerWaypoints = 0;
			}
		}
	}
	else
	{
		Game.player.x = Math.round(Game.player.x);
		Game.player.y = Math.round(Game.player.y);
	}
};